/***************************************************************************
*  Udpflea.cpp
*
*  July 2007
*  Copyright  2007  RuijinZheng,China
*  Email mebetop@126.com
****************************************************************************/

/*
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */
#include "Udpflea.hpp"
/////////////////////////////////
/// ------>  public funtions
Udpflealayer::~Udpflealayer(){
	  map<SocketType,Udpproxy *>::iterator iter;
	  for(iter=udpflealinks.begin(); iter!=udpflealinks.end(); iter++)
	  {
	      if((*iter).second!=NULL){
		      if(((*iter).second->tcpsocket)>0){
		    	  close((*iter).second->tcpsocket);
		      }
	   	   delete (*iter).second;
	      }
	  }
}

Udpflealayer *Udpflealayer::Getonlyinstance(const old_bind *origbind,const old_connect *origconnect,
             const old_recv *origrecv,const old_send *origsend,const old_recvfrom *origrecvfrom,
             const old_sendto *origsendto){
       try{
            static Udpflealayer udpflea(origbind,origconnect,origrecv,origsend,origrecvfrom,origsendto);
            return  &udpflea;
       }
       catch(const string &s){
            Lowlayer::Errordeliver(s,
                       strerror(errno));
           return NULL;
       }
}

const Proxyserver *Udpflealayer::Getproxyserver(const vector<Proxyserver> &availableserver)
{ 
       if(availableserver.size()<1)
            return NULL;
       return &availableserver.at(0);
}
SocketType Udpflealayer::Begintcpcommunicate(const vector<Proxyserver> &availableserver){
	   SocketType tcpsock=socket(AF_INET,SOCK_STREAM,0);
       bool actionresult=false;
       if(tcpsock<0){
    	      Errordeliver("error accrued while creating socket for tcp communicate",strerror(errno));
    	      return -1;
    	       
        }
       int sockflags = fcntl(tcpsock, F_GETFL);
       if(sockflags & O_NONBLOCK){
            if(fcntl(tcpsock,F_SETFL, sockflags-O_NONBLOCK)==-1)
            	   Errordeliver("error accrued while seting socket to blocking mode",strerror(errno));
       }
       const Proxyserver * proxyserver = Getproxyserver(availableserver);
       if(proxyserver!=NULL){
    	     if(Connecttoserver(tcpsock,proxyserver->sServername,proxyserver->iPort)<0){
    	    	   Errordeliver("error accrued while connecting to proxy server",strerror(errno));
    	    	   return -1;
      	  }else{
      		   actionresult=Prepareforsocks5proxy(tcpsock,proxyserver->sUser,proxyserver->sPasswd);
      	  }
        }else{
     	     Errordeliver("no suitable proxy server provied","please edit proxy server list solve this problem");
     	     return -1;
         }
       if(actionresult==true){
            return tcpsock;
       }else{
        	   return -1;
       }
}

ssize_t Udpflealayer::Sendtoflea(int sockfd,const void*buff,size_t buflen,int flags,const sockaddr  *to,
       socklen_t  tolen,const Udpproxy *responsestruct){

       char buf[UDPBUFFERSIZE+30];
       int ilen=0;
       memset(buf,0,UDPBUFFERSIZE+30);
       buf[0]=0x00;
       buf[1]=0x00;
       buf[2]=0x00;
       buf[3]=0x01;
       memcpy((void *)&buf[4],(void *)&(((sockaddr_in *)to)->sin_addr.s_addr),4);
       memcpy((void *)&buf[8],(void *)&(((sockaddr_in *)to)->sin_port),2);
       sockaddr_in addr;
       addr.sin_family=AF_INET;
       addr.sin_addr.s_addr=inet_addr(responsestruct->bindip.c_str());
       addr.sin_port=htons(responsestruct->bindport);
       if(addr.sin_addr.s_addr==INADDR_NONE){
           hostent *hp=gethostbyname(responsestruct->bindip.c_str());
              if(hp==NULL){
                  return -1;
                 }
           addr.sin_addr=*(( struct in_addr* )( hp->h_addr ));
         }
       memcpy(&buf[10],buff,buflen);
       int ret=-1;
       socklen_t lent=sizeof(sockaddr_in);
       ret=(*oldsendto)(sockfd,buf,buflen+10,0,(sockaddr *)&addr,sizeof(sockaddr));
       return ret;
}

ssize_t Udpflealayer::Recvfromflea(int sockfd,void *buff,size_t buflen,int flags,sockaddr *from,socklen_t *fromlen){

       char buf[UDPBUFFERSIZE];
       memset(buf,0,UDPBUFFERSIZE);
       sockaddr_in addr;
       int ret=-1;
       socklen_t socklen=sizeof(struct sockaddr_in);
       ret = (*oldrecvfrom)(sockfd,buf,UDPBUFFERSIZE,0,(sockaddr *)&addr,&socklen);
       if(ret==-1){
            return -1;
       }
       if(buf[3]==0x01){

            ((sockaddr_in *)from)->sin_family=AF_INET;
            ((sockaddr_in *)from)->sin_addr.s_addr=*((int *)&(buf[4]));
            ((sockaddr_in *)from)->sin_port=*((short *)&(buf[8]));
            ret-=10;
            memcpy(buff,&buf[10],ret);
       }
       else if(buf[3]==0x03){
            char ipbuf[BUFFERSIZE];
            memset(ipbuf,0,BUFFERSIZE);
            strncpy(ipbuf,&buf[5],buf[4]);
            hostent *hp=gethostbyname(ipbuf);
            if(hp==NULL){
                 return -1;
            }
            int ilen=4+buf[4];
            ((sockaddr_in *)from)->sin_family=AF_INET;
            ((sockaddr_in *)from)->sin_addr=*(( struct in_addr* )( hp->h_addr));
            ((sockaddr_in *)from)->sin_port=*((short *)&(buf[ilen]));
            ret-=(ilen+2);
            
            memcpy((void *)buff,(void *)&(buf[ilen]),ret);
            ret-=(4+2+1+buf[4]);
       }
     else{
            return -1;
     }
    return ret;
    
}
Udpproxy *Udpflealayer::Initialresponseaddr(const int &sock,const SocketType & tcpsock,const struct sockaddr *addr){
       unsigned char buf[UDPTCPBUFFERSIZE];
       int sendlen;
       int ret;
       Udpproxy *responsestruct;
       memset(buf,0,UDPTCPBUFFERSIZE);
       sendlen = Makesocks5destrequest_udp(buf,(*(struct sockaddr_in *)addr));
       if(!Sendtoserver(tcpsock,(const char*)buf,sendlen)){
    	      Errordeliver("exception accrued while sending information to proxy server",strerror(errno));
    	      return NULL;
       }
       memset(buf,0,UDPTCPBUFFERSIZE);
       ret = Recvfromserver(tcpsock,(char*)buf,UDPTCPBUFFERSIZE);
       if(buf[ 0 ]!= 0x05 || buf[ 2 ] != 0x00 ){
    	     Errordeliver("received bad response from proxy server",strerror(errno));
    	     return NULL;
       }
       if(buf[1] == 0x00 ){
             switch(buf[3]){
                case 0x01:{
                     int i_ip=*((int *)&buf[4]);
                     in_addr in;
                     in.s_addr=i_ip;
                     char ipchars[20];
                     memset((void *)ipchars,0,20);
                     strcpy(ipchars,inet_ntoa(in));
                     responsestruct=new Udpproxy;
                     responsestruct->bindip=ipchars;
                     responsestruct->bindport=ntohs(*((short *)&(buf[8])));
                     responsestruct->addrtype=0x01;
                     responsestruct->tcpsocket=tcpsock;  
                     udpflealinks.insert(Pair(sock,responsestruct));
                     return responsestruct;
                }
                case 0x03:{
                     char cip[buf[4]];
                     strncpy(cip,(const char*)(&buf[5]),buf[4]);
                     responsestruct=new Udpproxy;
                     responsestruct->bindip=cip;
                     responsestruct->bindport=ntohs(*((short *)&buf[5+buf[4]]));
                     responsestruct->addrtype=0x03;
                     responsestruct->tcpsocket=tcpsock;
                     udpflealinks.insert(Pair(sock,responsestruct));
                     return responsestruct;
                }
                default:
                	Errordeliver("proxy server returned bad method",strerror(errno));
                	return NULL;
            }
       }
       else if(buf[1] == 0x01 ){
    	     Errordeliver("proxy server return :common socks error ocured",strerror(errno));
    	     return NULL;
       }
       else if(buf[1] == 0x02 ){
    	     Errordeliver("proxy server return :connection not allowed",strerror(errno));
    	     return NULL;
       }
       else if(buf[1] == 0x03 ){
    	     Errordeliver("proxy server return :net unreachable",strerror(errno));
    	     return NULL;
       }
       else if(buf[1] == 0x04 ){
    	     Errordeliver("proxy server return :host unreachable",strerror(errno));
    	     return NULL;
       }
       else if(buf[1] == 0x05 ){
    	     Errordeliver("proxy server return :connection refused",strerror(errno));
    	     return NULL;
       }
       else if(buf[1] == 0x06 ){
    	     Errordeliver("proxy server return :TTL timeout",strerror(errno));
    	     return NULL;
       }
       else if ( buf[ 1 ] == 0x07 ) {
    	     Errordeliver("proxy server return :command not supported",strerror(errno));
    	     return NULL;
       }
       else if ( buf[ 1 ] == 0x08 ){
    	     Errordeliver("proxy server return :address type not supported",strerror(errno));
    	     return NULL;
       }
       else{
    	     Errordeliver("proxy server return :unkown error",strerror(errno));
    	     return NULL;
       }
    
}
/// <------  public funtions
///////////////////////////////
///////////////////////////////
/// ------>  protected funtions

Udpflealayer::Udpflealayer(const old_bind *origbind,const old_connect *origconnect,const old_recv *origrecv,
          const old_send *origsend,const old_recvfrom *origrecvfrom,const old_sendto *origsendto):
          Lowlayer(origconnect,origrecv,origsend,origrecvfrom,origsendto){
       pthread_mutex_init(&mut,NULL);
       char *maxportchars=getenv("MAXPORT");
       if(maxportchars==NULL){
            maxport=60000;
       }
       else{
            maxport=atoi(maxportchars);
       }
       char *minportchars=getenv("MINPORT");
       if(minportchars==NULL){
            minport=40000;
       }
       else{
            minport=atoi(minportchars);
       }
}
sockaddr_in * Udpflealayer::Getbindedaddr(const int &sock){
	  sockaddr_in *zeroaddr=new sockaddr_in;
	  socklen_t len = sizeof(struct sockaddr_in);
	  if(getsockname(sock, (struct sockaddr *)zeroaddr, &len)!=0){
			 Lowlayer::Errordeliver("error accrued while calling getsockname()",strerror(errno));
			 delete zeroaddr;
			 return NULL;
	  }else{
		  		if(ntohs(zeroaddr->sin_port)!=0){
			  		return zeroaddr;
		  		}else{
		  				///////// after calling "sendto" function,
		  			   ////  socket will be binded to a socket address,
		  			  ///// so here I sendto to sendto something to 
		  			 ////  a random address ,so that I can get which 
		  			 //// address the socket has binded to.
		  				inet_aton("127.0.0.1",&(zeroaddr->sin_addr));
		  				zeroaddr->sin_family=AF_INET;
		  				zeroaddr->sin_port=htons(getramdomport());
		  				if(((*oldsendto)(sock,"netflea",7,0,(sockaddr *)zeroaddr,sizeof(sockaddr)))<0){
				  			delete zeroaddr;
		  					return false;
		  				}
			  			delete zeroaddr;
		  				sockaddr_in *localaddr=new sockaddr_in;
		  				socklen_t len = sizeof(struct sockaddr_in);
		  				if(getsockname(sock, (struct sockaddr *)localaddr, &len)!=0){
		  						Lowlayer::Errordeliver("error accrued while calling getsockname()",strerror(errno));
		  						delete localaddr;
		  						return NULL;
		  				}else{
		  						return localaddr;
		  				}
		  		}
	  }

}
Udpproxy *Udpflealayer::GetUdplayerReady(int & sock,const struct sockaddr *addr,const vector<Proxyserver> &availableserver){
		if(addr==NULL){
			return NULL;
		}
	    SocketType tcpsocket;
	    Udpproxy *response;
		if((tcpsocket=Begintcpcommunicate(availableserver))!=-1){
			if((response=Initialresponseaddr(sock,tcpsocket,addr))==NULL){
				Lowlayer::Errordeliver("error accrued while calling Initialresponseaddr()",strerror(errno));
				close(tcpsocket);
				return NULL;
			}else{
				
				cout<<"------------------------------------------------------------------"<<endl;
				cout<<"|  socket :"<<sock<<" has established connection to proxy server |"<<endl;
				cout<<"------------------------------------------------------------------"<<endl;
				return response;
			}
		}else{
			 Lowlayer::Errordeliver("error accrued while calling Begintcpcommunicate()",strerror(errno));
			 return NULL;
		} 
}
Udpproxy *Udpflealayer::Getreadyresponseaddr(SocketType &sock){
	 map<SocketType,Udpproxy *>::iterator it=udpflealinks.begin();
    it=udpflealinks.find(sock);
    if(it!=udpflealinks.end()){
 	    return (*it).second;
    }
    else{
    	 return NULL;
    }
}

/// <------  protected funtions
///////////////////////////////
///////////////////////////////
/// ------>  private funtions
int Udpflealayer::getramdomport() const{
        srand(time(0));
        return (minport+rand()%(maxport-minport));

}

/// <------  private funtions
//////////////////////////////
